/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.escom.healthlc.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

/**
 *
 * @author Carlos
 */
public class PropiedadesMemoria {

    private int noPatronesTotal;
    private int noElementosPorPatron;
    private int[] noPatronesPorClase;
    private double eficienciaTotal;
    private static final int NUMERO_ELEMENTOS_POR_PATRON = 60;
    private static final int NUMERO_DE_CLASES = 5;

    public double getEficienciaTotal() {
        return eficienciaTotal;
    }

    public int getNoElementosPorPatron() {
        return noElementosPorPatron;
    }

    public void setNoElementosPorPatron(int noElementosPorPatron) {
        this.noElementosPorPatron = noElementosPorPatron;
    }

    public int getNoPatronesTotal() {
        return noPatronesTotal;
    }

    public void setNoPatronesTotal(int noPatronesTotal) {
        this.noPatronesTotal = noPatronesTotal;
    }
    
    public int[] getNoPatronesPorClase() {
        return noPatronesPorClase;
    }

    public void setNoPatronesPorClase(int[] noPatronesPorClase) {
        this.noPatronesPorClase = noPatronesPorClase;
    }

    /**
     * El método DataInit, inicia todas las variables necesarias para poder
     * continuar con los métodos propios de la memoria.
     * <p>
     * <b>Nota:</b> El archivo debe estar conformado de la siguiente manera:
     * Cada patrón debe ser separado por un salto de línea, cada elemento del
     * patron debe ser separado por un espacio.
     *
     * @param fileDataIn BufferedReader que contiene los patrones de entrada.
     * @since 1.0
     */
    public void DataInit(BufferedReader fileDataIn) throws IOException {
        String[] lineaLeida;
        HashMap<String, Integer> mapClases = new HashMap();
        int aum = 0;
        int sizeLine;
        noPatronesTotal = 0;
        noElementosPorPatron = 0;
        while (fileDataIn.ready()) {
            noPatronesTotal++;
            lineaLeida = fileDataIn.readLine().split(" ");
            sizeLine = lineaLeida.length;
            String claseKey = lineaLeida[sizeLine - 1];
            mapClases.put(claseKey, (mapClases.get(claseKey) != null) ? mapClases.get(claseKey) + 1 : 1);
            if (noElementosPorPatron == 0) {
                noElementosPorPatron = sizeLine - 1;
            }
        }
//        noClases = mapClases.size();
        noPatronesPorClase = new int[NUMERO_DE_CLASES];
        System.out.println("Numero de clases: " + NUMERO_DE_CLASES);
        while (aum < NUMERO_DE_CLASES) {
            noPatronesPorClase[aum] = mapClases.get(String.valueOf(aum));//Numero de Patrones por clase
            System.out.println("Numero de Patrones clase (" + aum + "): " + noPatronesPorClase[aum]);
            aum++;
        }

        System.out.println("Elementos por Patron: " + noElementosPorPatron);
        System.out.println("Patrones Totales: " + noPatronesTotal);
    }

    /**
     * El método ExtraccionPatronesMask.
     *
     * @param fileDataIn BufferedReader que contiene los patrones de entrada.
     * @param noElementosPorPatron
     * @param binarioMask
     * @return Una variable double[N][M], la cual contiene los patrones.
     * @throws java.io.IOException
     * @throws <b>Error_de_compilacion</b> Se causa cuando no es invocado el
     * metodo
     * {@link memoria.PropiedadesMemoria#DataInit(java.io.BufferedReader)}
     * previamente.
     * @since 1.0
     */
    public double[][] ExtraccionPatronesMask(BufferedReader fileDataIn, int noElementosPorPatron, boolean[] binarioMask) throws IOException {
        int i = 0;
        int j = 0;
        String[] patronString;
        double[][] patronesAllMatriz = new double[noPatronesTotal][noElementosPorPatron];
        while (fileDataIn.ready()) {
            patronString = fileDataIn.readLine().split(" ");
            for (int l = 0; l < patronString.length - 1; l++) {//(patronString.length - 1)Quita la ultima columna
                if (binarioMask[l]) {
                    patronesAllMatriz[i][j++] = Double.valueOf(patronString[l]);
                }
            }
            j = 0;
            i++;
        }
//        System.out.println("Patrones");
//        for (int k = 0; k < patronesAllMatriz.length; k++) {
//            for (int l = 0; l < patronesAllMatriz[k].length; l++) {
//                System.out.print(patronesAllMatriz[k][l] + "\t");
//            }
//            System.out.println("");
//        }
        return patronesAllMatriz;
    }

    /**
     * El método ExtraccionPatrones, extrae los patrones del BufferedReader,
     * para almacenarlos en una variable de tipo double[ ][ ].
     * <p>
     * <b>Nota:</b> El archivo debe estar conformado de la siguiente manera:
     * Cada patrón debe ser separado por un salto de línea, cada elemento del
     * patrón debe ser separado por un espacio.
     *
     * @param fileDataIn BufferedReader que contiene los patrones de entrada.
     * @return Una variable double[N][M], la cual contiene los patrones.
     * @throws <b>Error_de_compilacion</b> Se causa cuando no es invocado el
     * metodo
     * {@link memoria.PropiedadesMemoria#DataInit(java.io.BufferedReader)}
     * previamente.
     * @since 1.0
     */
    public double[][] ExtraccionPatrones(BufferedReader fileDataIn) throws IOException {
        int i = 0;
        String[] patronString;
        double[][] patronesAllMatriz = new double[noPatronesTotal][noElementosPorPatron];
        while (fileDataIn.ready()) {
            patronString = fileDataIn.readLine().split(" ");
            for (int l = 0; l < patronString.length - 1; l++) {//(patronString.length - 1)Quita la ultima columna
                patronesAllMatriz[i][l] = Double.valueOf(patronString[l]);
            }
            i++;
        }
//        System.out.println("Patrones");
//        for (int k = 0; k < patronesAllMatriz.length; k++) {
//            for (int l = 0; l < patronesAllMatriz[k].length; l++) {
//                System.out.print(patronesAllMatriz[k][l] + "\t");
//            }
//            System.out.println("");
//        }
        return patronesAllMatriz;
    }

    /**
     * El método CalcularVectorMedio, calcula el vector medio basándose en todos
     * los patrones previamente adquiridos por el método
     * {@link memoria.PropiedadesMemoria#ExtraccionPatrones(java.io.BufferedReader)}
     * los cuales deben ser pasado al parámetro que recibe el método
     * <p>
     *
     * @param patronesAllMatriz Matriz de tipo double que contiene los patrones
     * de entrada.
     * @return Una variable double[N], la cual es el vector medio de los
     * patrones.
     * @throws <b>Error_de_compilacion</b> Se causa cuando no es invocado el
     * metodo
     * {@link memoria.PropiedadesMemoria#DataInit(java.io.BufferedReader)}
     * previamente.
     * @since 1.0
     */
    public double[] CalcularVectorMedio(double[][] patronesAllMatriz) {
        int j;
        double[] vectorMedio = new double[noElementosPorPatron];
        for (int l = 0; l < patronesAllMatriz.length; l++) {
            j = 0;
            for (int m = 0; m < patronesAllMatriz[l].length; m++) {
                vectorMedio[j++] += patronesAllMatriz[l][m];
            }
        }
//        System.out.println("Tamños: " + patronesAllMatriz.length);
        for (int k = 0; k < vectorMedio.length; k++) {
            vectorMedio[k] = vectorMedio[k] / patronesAllMatriz.length;
        }
//        System.out.println("Vector Medio");
//        for (int k = 0; k < vectorMedio.length; k++) {
//            System.out.print(vectorMedio[k] + "\t");
//        }
//        System.out.println("");
        return vectorMedio;
    }

    /**
     * El método CalcularPatronesTrasladados, toma como parámetros la matriz de
     * patrones previamente obtenida por el método
     * {@link memoria.PropiedadesMemoria#ExtraccionPatrones(java.io.BufferedReader)}
     * y el vector medio obtenido también previamente por el método
     * {@link memoria.PropiedadesMemoria#CalcularVectorMedio(double[][])}.
     * <p>
     * Dentro del método se calculan los vectores trasladados.
     *
     * @param patronesAllMatriz Matriz de tipo double que contiene los patrones
     * de entrada.
     * @param vectorMedio Array que contiene el vector medio
     * @return Una variable double[N][M], la cual contiene los patrones
     * trasladados.
     * @throws <b>Error_de_compilacion</b> Se causa cuando no es invocado el
     * método
     * {@link memoria.PropiedadesMemoria#DataInit(java.io.BufferedReader)}
     * previamente.
     * @since 1.0
     */
    public double[][] CalcularPatronesTrasladados(double[][] patronesAllMatriz, double[] vectorMedio) {
        int i;
        double[][] patronesTrasladoAllMatriz = new double[noPatronesTotal][noElementosPorPatron];
        for (int k = 0; k < patronesAllMatriz.length; k++) {
            i = 0;
            for (int l = 0; l < patronesAllMatriz[k].length; l++) {
                patronesTrasladoAllMatriz[k][l] = patronesAllMatriz[k][l] - vectorMedio[i++];
            }
        }
//                System.out.println("Patrones Trasladados");
//        for (int k = 0; k < patronesTrasladoAllMatriz.length; k++) {
//            for (int l = 0; l < patronesTrasladoAllMatriz[k].length; l++) {
//                System.out.print(patronesTrasladoAllMatriz[k][l] + "\t");
//            }
//            System.out.println("");
//        }
        return patronesTrasladoAllMatriz;

    }

    /**
     * El método CalcularMemoria, toma como parámetros la matriz de patrones
     * trasladado previamente obtenida por el método
     * {@link memoria.PropiedadesMemoria#CalcularPatronesTrasladados(double[][], double[]) }.
     * <p>
     * Dentro del método se calcula la memoria la cual devulve una matriz de
     * tipo double.
     *
     * @param patronesAllMatriz Matriz de tipo double que contiene los patrones
     * trasladados.
     * @return Una variable double[N][M], la cual contiene la memoria asocitiva
     * @throws <b>Error_de_compilacion</b> Se causa cuando no es invocado el
     * método
     * {@link memoria.PropiedadesMemoria#DataInit(java.io.BufferedReader)}
     * previamente.
     * @since 1.0
     */
    public double[][] CalcularMemoria(double[][] patronesTrasladoAllMatriz) {
        int i = 0;
        int aum = 0;
        double[][] memoriaMatriz = new double[NUMERO_DE_CLASES][noElementosPorPatron];
        for (int k = 0; k < patronesTrasladoAllMatriz.length; k++) {
            for (int l = 0; l < patronesTrasladoAllMatriz[k].length; l++) {
                memoriaMatriz[i][l] += patronesTrasladoAllMatriz[k][l];
            }
            aum++;
            if (noPatronesPorClase[i] == aum) {
                aum = 0;
                i++;
            }
        }
//        System.out.println("\nMemoria\n");
//        for (int k = 0; k < memoriaMatriz.length; k++) {
//            for (int l = 0; l < memoriaMatriz[k].length; l++) {
//                System.out.print(memoriaMatriz[k][l] + "\t");
//            }
//            System.out.println("");
//        }
        return memoriaMatriz;
    }

    public List<String> ClasificarPatrones(double[][] patronesTraslado, double[][] memoriaMatriz) {
        double maximoRecuperado;
        double[] claseRecuperada;
        List<String> patronRecuperadoLabel = new ArrayList<String>();
        int i, numeroClaseRecuperada = 0, noClasesI = 0, countPatrones = 0;
        eficienciaTotal = 0;
        for (int m = 0; m < patronesTraslado.length; m++) {
            claseRecuperada = new double[NUMERO_DE_CLASES];
            i = 0;
            countPatrones++;
            for (int k = 0; k < memoriaMatriz.length; k++) {
                for (int l = 0; l < memoriaMatriz[k].length; l++) {
                    claseRecuperada[i] += memoriaMatriz[k][l] * patronesTraslado[m][l];
                }
                i++;
            }
            maximoRecuperado = Integer.MIN_VALUE;
            for (int k = 0; k < claseRecuperada.length; k++) {
                if (claseRecuperada[k] > maximoRecuperado) {
                    maximoRecuperado = claseRecuperada[k];
                    numeroClaseRecuperada = k;
                }
            }
//            patronRecuperadoLabel.add(numeroClaseRecuperada + "," + maximoRecuperado);
            patronRecuperadoLabel.add(numeroClaseRecuperada + "");
        }
        return patronRecuperadoLabel;
    }

    /**
     * El método RecuperacionData.
     *
     * @param patronesTrasladoAllMatriz
     * @param memoriaMatriz
     * @since 1.0
     */
    public void RecuperacionData(double[][] patronesTrasladoAllMatriz, double[][] memoriaMatriz) {
        double maximoRecuperado;
        double[] claseRecuperada;
        double[] eficienciaTotalPorClase = new double[NUMERO_DE_CLASES];
        double[][] recuperacionPorClase = new double[NUMERO_DE_CLASES][NUMERO_DE_CLASES];
        int i, numeroClaseRecuperada = 0, noClasesI = 0, countPatrones = 0;
        eficienciaTotal = 0;
        for (int m = 0; m < patronesTrasladoAllMatriz.length; m++) {
            claseRecuperada = new double[NUMERO_DE_CLASES];
            i = 0;
            countPatrones++;
            for (int k = 0; k < memoriaMatriz.length; k++) {
                for (int l = 0; l < memoriaMatriz[k].length; l++) {
                    claseRecuperada[i] += memoriaMatriz[k][l] * patronesTrasladoAllMatriz[m][l];
                }
                i++;
            }
            maximoRecuperado = Integer.MIN_VALUE;
            for (int k = 0; k < claseRecuperada.length; k++) {
                if (claseRecuperada[k] > maximoRecuperado) {
                    maximoRecuperado = claseRecuperada[k];
                    numeroClaseRecuperada = k;
                }
            }
            recuperacionPorClase[noClasesI][numeroClaseRecuperada]++;
            if (countPatrones == noPatronesPorClase[noClasesI]) {
                noClasesI++;
                countPatrones = 0;
            }
        }
        System.out.println("Matriz de Confusion");
        for (int j = 0; j < recuperacionPorClase.length; j++) {
            for (int k = 0; k < recuperacionPorClase[j].length; k++) {
                System.out.print(recuperacionPorClase[j][k] + "\t");
            }
            System.out.println("");
        }
        for (int j = 0; j < recuperacionPorClase.length; j++) {
            for (int k = 0; k < recuperacionPorClase[j].length; k++) {
                if (j == k) {
                    eficienciaTotalPorClase[j] = (recuperacionPorClase[j][k] * 100) / noPatronesPorClase[j];
                }
            }
        }
        for (int j = 0; j < eficienciaTotalPorClase.length; j++) {
            eficienciaTotal += eficienciaTotalPorClase[j];
        }
        eficienciaTotal = eficienciaTotal / NUMERO_DE_CLASES;
//        System.out.println("" + eficienciaTotal);
    }

    public void RecuperacionDataTwo(double[][] patronesTrasladoAllMatriz, double[][] memoriaMatriz) {
        double maximoRecuperado;
        double[] claseRecuperada;
        double[][] recuperacionPorClase = new double[NUMERO_DE_CLASES][NUMERO_DE_CLASES];
        int i, numeroClaseRecuperada = 0, noClasesI = 0, countPatrones = 0;
        eficienciaTotal = 0;
        for (int l = 0; l < patronesTrasladoAllMatriz.length; l++) {
            claseRecuperada = new double[NUMERO_DE_CLASES];
            i = 0;
            countPatrones++;
            for (int j = 0; j < memoriaMatriz.length; j++) {
                for (int k = 0; k < memoriaMatriz[j].length; k++) {
                    claseRecuperada[i] += memoriaMatriz[j][k] * patronesTrasladoAllMatriz[l][k];
                }
                i++;
            }
            maximoRecuperado = Integer.MIN_VALUE;
            for (int k = 0; k < claseRecuperada.length; k++) {
                if (claseRecuperada[k] > maximoRecuperado) {
                    maximoRecuperado = claseRecuperada[k];
                    numeroClaseRecuperada = k;
                }
            }
            recuperacionPorClase[noClasesI][numeroClaseRecuperada]++;
            if (countPatrones == noPatronesPorClase[noClasesI]) {
                noClasesI++;
                countPatrones = 0;
            }
        }
        for (int j = 0; j < recuperacionPorClase.length; j++) {
            for (int k = 0; k < recuperacionPorClase[j].length; k++) {
                if (j == k) {
                    eficienciaTotal += recuperacionPorClase[j][k];
                }
            }
        }
        eficienciaTotal = (eficienciaTotal * 100) / noPatronesTotal;
    }

    public void EscribirMatriz(double[][] matrizDoble, BufferedWriter fileDataOut) throws IOException {
        for (int k = 0; k < matrizDoble.length; k++) {
            for (int l = 0; l < matrizDoble[k].length; l++) {
                fileDataOut.write(matrizDoble[k][l] + " ");
            }
            fileDataOut.write("\n");
        }
        fileDataOut.close();
    }

    public void EscribirArreglo(double[] matrizDoble, BufferedWriter fileDataOut) throws IOException {
        for (int k = 0; k < matrizDoble.length; k++) {
            fileDataOut.write(matrizDoble[k] + " ");
        }
        fileDataOut.close();
    }

    public double[][] LeerMemoria(BufferedReader fileDataIn) throws IOException {
        int i = 0;
        int j = 0;
        String[] patronString;
        double[][] memoriaMatriz = new double[NUMERO_DE_CLASES][noElementosPorPatron];
        System.out.println("" + fileDataIn.ready());
        while (fileDataIn.ready()) {
            patronString = fileDataIn.readLine().split(" ");
            for (int l = 0; l < patronString.length; l++) {
                memoriaMatriz[i][j++] = Double.valueOf(patronString[l]);
            }
            i++;
            j = 0;
        }
        return memoriaMatriz;
    }

    public double[][] LeerPatronesTrasladados(BufferedReader fileDataIn) throws IOException {
        int i = 0;
        int j = 0;
        String[] patronString;
        double[][] patronesTrasladoAllMatriz = new double[noPatronesTotal][noElementosPorPatron];
        while (fileDataIn.ready()) {
            patronString = fileDataIn.readLine().split(" ");
            for (int l = 0; l < patronString.length; l++) {
                patronesTrasladoAllMatriz[i][j++] = Double.valueOf(patronString[l]);
            }
            i++;
            j = 0;
        }
//        System.out.println("Patrones Trasladados");
//        for (int k = 0; k < patronesTrasladoAllMatriz.length; k++) {
//            for (int l = 0; l < patronesTrasladoAllMatriz[k].length; l++) {
//                System.out.print(patronesTrasladoAllMatriz[k][l] + "\t");
//            }
//            System.out.println("");
//        }
        return patronesTrasladoAllMatriz;
    }

    public double[] LeerVectorMedio(BufferedReader fileDataIn) throws IOException {
        String[] patronString;
        double[] vectorMedio = new double[NUMERO_ELEMENTOS_POR_PATRON];
        while (fileDataIn.ready()) {
            patronString = fileDataIn.readLine().split(" ");
            for (int k = 0; k < vectorMedio.length; k++) {
                vectorMedio[k] = Double.valueOf(patronString[k]);

            }
        }
        return vectorMedio;
    }
}
